-- 2022-8-17

--[[ 18.5 迭代器的真实含义

    "迭代器"这个名称多少有点误导性 这是因为迭代器并没有进行实际的迭代:真正的迭代是for循环完成的 迭代器只不过为每次的迭代提供连续的值
    或许 称其为"生成器(generator)"更好 表示为迭代生成(generate)元素 不过 "迭代器"这个名字已在诸如Java等其他语言中被广泛使用了

    然而 还有一种创建迭代器的方式可以让迭代器进行实际的迭代操作 当使用这种迭代器时 就不再需要编写循环了
    相反 只需要调用这个迭代器 并传入一个描述了在每次迭代时迭代器需要做什么的参数即可
    更确切的说 迭代器连接一个函数作为参数 这个函数在循环的内部被调用 这种迭代器就被称为真正的迭代器(true iterator)

    举一个更具体的例子 让我们使用这种风格再次重写allwords迭代器:
    function allwords (f)
        for line in io.lines() do
            for word in string.gmatch(line,"%w+") do
                f(word) -- 调用函数
            end
        end
    end
    使用这个迭代器 我们必须传入一个函数作为循环体 如果我们只想输入每个单词 那么简单地使用函数print即可:
    allwords(print)

    通常 我们可以使用一个匿名函数作为循环体 例如 以下的代码用于计算单词"hello"在输入文件中出现的次数:
    local count = 0
    allwords(function (w)
        if w == "hello" then count = count + 1 end
    end)
    print(count)
    同样的需求 如果采用之前的迭代器风格 差异也不是特别大:
    local count = 0
    for w in allwords() do
        if w == "hello" then count = count + 1 end
    end
    print(count)

    真正的迭代器在老版本的Lua语言中曾非常流行 那时还没有for语句
    真正的迭代器与生成器风格(generator-style)的迭代器相比怎么样呢？这两种风格都有大致相同的开销 即每次迭代都有一次函数调用
    一方面 编写真正的迭代器比较容易(不过 我们可以使用24.3节中的方法使用协程来弥补) 另一方面 生成器风格的迭代器则更灵活
    首先 生成器风格的迭代器则允许两个或更多个并行的迭代(例如 考虑逐个单词比较两个文件的迭代器)
    其次 生成器风格的迭代器允许在循环体中使用break和return语句
    使用真正的迭代器 return语句从匿名函数中返回而并非从进行迭代的函数中返回 基于这些原因 笔者一般更喜欢生成器风格的迭代器
]]